﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using PasteCodeTaskBase;
using PasteTimer.taskmodels;
using Volo.Abp.AspNetCore.Mvc;
using Z.EntityFramework.Plus;

namespace PasteTimer
{
    /// <summary>
    /// 
    /// </summary>
    [ApiController]
    [Route("/api/task/[action]")]
    public class TaskController : AbpController
    {
        private ChannelHelper _channelHelper => LazyServiceProvider.LazyGetRequiredService<ChannelHelper>();

        private readonly IPasteTimerDbContext _dbContext;

        private IAppCache _cache => LazyServiceProvider.LazyGetRequiredService<IAppCache>();

        //private TaskConfig _config;

        //private readonly ILogger<TaskController> _logger;

        private ModelHelper _modelHelper => LazyServiceProvider.LazyGetRequiredService<ModelHelper>();

        /// <summary>
        /// 
        /// </summary>
        /// <param name="dbContext"></param>
        public TaskController(IPasteTimerDbContext dbContext)
        {
            //_channelHelper = task;
            _dbContext = dbContext;
            //_cache = cache;
            //_config = config.Value;
            //_logger = logger;
            //_modelHelper = modelHelper;
        }

        /// <summary>
        /// 任务执行回传
        /// </summary>
        /// <returns></returns>
        [HttpPost]
        public async Task<int> callback(PasteTaskCallBackModel input, [FromServices] IOptions<TaskConfig> config)
        {
            var _config = config.Value;
            Logger.LogInformation($"callback:{Newtonsoft.Json.JsonConvert.SerializeObject(input)}");
            //_logger.LogInformation(Newtonsoft.Json.JsonConvert.SerializeObject(input));
            checkmodel(_config);
            if (String.IsNullOrEmpty(input.message) && input.code == 200)
            {
                input.message = "success!";
            }
            var result = await _dbContext.TaskLog.Where(x => x.Id == input.logid).UpdateAsync(x => new TaskLog
            {
                Durtion = input.duration,
                Status = (TaskStatus)input.code,
                Message = input.message
            });
            if (input.taskid != 0)
            {
                if (input.code == (int)TaskStatus.success)
                {
                    _modelHelper.CollectData(input.taskid, CollectType.okay);
                    //_cache.HashIncrementAsync(PublicString.CollectHashKey, string.Format(PublicString.CollectFieldFormat, $"{DateTime.Now.ToString("yyyyMMddHH")}", input.taskid, "okay"));
                }
                else
                {
                    _modelHelper.CollectData(input.taskid, CollectType.fail);
                    //_cache.HashIncrementAsync(PublicString.CollectHashKey, string.Format(PublicString.CollectFieldFormat, $"{DateTime.Now.ToString("yyyyMMddHH")}", input.taskid, "fail"));
                    _channelHelper.WriteNotice(new noticemodels.NoticeLogAddDto() { Body = $"任务:{input.taskid} 执行失败！ 异常：{input.code}", Code = "workfail", ObjId = input.nodeid });
                }
            }
            return 1;
        }

        /// <summary>
        /// 子节点加入到当前服务 如果是集群模式，则判定是否要往master中发送这个任务，由master分发任务
        /// </summary>
        /// <returns></returns>
        [HttpPost]
        public async Task<NodeInfoDto> join(NodeInfoAddDto input, [FromServices] IOptions<TaskConfig> config)
        {
            if (String.IsNullOrEmpty(input.Group))
            {
                input.Group = "default";
            }
            var _config = config.Value;
            checkmodel(_config);
            //Console.WriteLine($"{DateTime.Now} node:{input.Url} start to join");
            if (String.IsNullOrEmpty(input.Url))
            {
                throw new Exception("Url is Empty!");
            }
            //基于URL加入是不是有点？？ 要基于jointoken？
            var find = _dbContext.NodeInfo.Where(x => x.Url == input.Url).FirstOrDefault();
            if (find == null || find == default)
            {
                if (String.IsNullOrEmpty(input.JoinToken))
                {
                    input.JoinToken = Guid.NewGuid().ToString().Replace("-", "").ToLower();
                }
                var node = ObjectMapper.Map<NodeInfoAddDto, NodeInfo>(input);
                node.IsEnable = true;
                node.Status = NodeStatus.running;
                node.Token = Guid.NewGuid().ToString().Replace("-", "").ToLower();
                node.Group = input.Group;
                if (!String.IsNullOrEmpty(input.ClientCode))
                {
                    var codes = new List<string>();
                    codes.Add(input.ClientCode);
                    node.ClientCodes = Newtonsoft.Json.JsonConvert.SerializeObject(codes);
                }
                _dbContext.Add(node);
                await _dbContext.SaveChangesAsync();
                var dto = ObjectMapper.Map<NodeInfo, NodeInfoDto>(node);
                _channelHelper.WriteStatus(new StatusModel() { Action = 1, body = Newtonsoft.Json.JsonConvert.SerializeObject(dto), ModelType = 2 });
                return dto;
            }
            else
            {
                //这里需要校验token是否正确，如果不正确，则需要提醒 有stoken进行校验，可以忽略
                if (!String.IsNullOrEmpty(find.ClientCodes))
                {
                    if (!String.IsNullOrEmpty(input.ClientCode))
                    {
                        var _array = Newtonsoft.Json.JsonConvert.DeserializeObject<List<string>>(find.ClientCodes);
                        _array.Insert(0, input.ClientCode);
                        if (_array.Count > 3)
                        {
                            _array.RemoveAt(3);
                        }
                        find.ClientCodes = Newtonsoft.Json.JsonConvert.SerializeObject(_array);
                    }
                }
                else
                {
                    if (!String.IsNullOrEmpty(input.ClientCode))
                    {
                        var _array = new List<string> { input.ClientCode };
                        find.ClientCodes = Newtonsoft.Json.JsonConvert.SerializeObject(_array);
                    }
                }
                find.Status = NodeStatus.running;
                find.IsEnable = true;
                var dto = ObjectMapper.Map<NodeInfo, NodeInfoDto>(find);
                //这里给与token的话变成泄露了... .. .
                dto.JoinToken = find.JoinToken;
                _channelHelper.WriteStatus(new StatusModel() { Action = 2, body = Newtonsoft.Json.JsonConvert.SerializeObject(dto), ModelType = 2 });
                find.Status = NodeStatus.running;
                await _dbContext.SaveChangesAsync();
                return dto;
            }
        }

        /// <summary>
        /// 离开当前服务 节点离开当前服务 可支持clientcode模式
        /// </summary>
        /// <param name="input"></param>
        /// <param name="config"></param>
        /// <returns></returns>
        [HttpPost]
        public async Task<int> leave(NodeInfoAddDto input, [FromServices] IOptions<TaskConfig> config)
        {
            var _config = config.Value;
            //如果是集群的话，这里是有问题的，会全部标记下线了，由于集群的信息是一样的...
            //可以添加一个附加码，至于集群中谁的附加码是多少由部署自己决定 ClientCodes
            if (!String.IsNullOrEmpty(input.Group))
            {
                input.Group = "default";
            }
            checkmodel(_config);
            var find = _dbContext.NodeInfo.Where(x => x.Url == input.Url).FirstOrDefault();
            if (find != null && find != default)
            {
                if (!String.IsNullOrEmpty(find.ClientCodes) && !String.IsNullOrEmpty(input.ClientCode))
                {
                    var _array = Newtonsoft.Json.JsonConvert.DeserializeObject<List<string>>(find.ClientCodes);
                    if (_array != null && _array.Count > 0)
                    {
                        _array.Remove(input.ClientCode);
                    }
                    find.ClientCodes = Newtonsoft.Json.JsonConvert.SerializeObject(_array);
                    if (_array == null || _array.Count == 0)
                    {
                        find.Status = NodeStatus.stop;
                        find.IsEnable = true;
                        _channelHelper.WriteStatus(new StatusModel() { Action = 3, body = $"{find.Id}", ModelType = 2 });
                    }
                    await _dbContext.SaveChangesAsync();
                }
                else
                {
                    find.Status = NodeStatus.stop;
                    find.IsEnable = true;
                    await _dbContext.SaveChangesAsync();
                    _channelHelper.WriteStatus(new StatusModel() { Action = 3, body = $"{find.Id}", ModelType = 2 });
                }
            }
            return 1;
        }


        /// <summary>
        /// 子节点申请添加一个任务 如何排重???
        ///</summary>
        /// <param name="input"></param>
        /// <returns></returns>
        [HttpPost]
        public async Task<TaskInfoDto> add(TaskInfoAddDto input)
        {
            if (String.IsNullOrEmpty(input.Groups))
            {
                throw new PasteTimerException("远端创建的任务需要指定节点，表示使用哪个节点执行这个任务！");
            }
            if (input.Groups.Contains(","))
            {
                throw new PasteTimerException("远端任务，不支持多个节点执行，只能指定其中一个节点执行！");
            }

            if (input.Groups == "*" || input.Groups == "slave")
            {
                throw new PasteTimerException("远端创建的任务不允许指定*或者slave节点执行任务！");
            }

            if (String.IsNullOrEmpty(input.MutexCode))
            {
                throw new PasteTimerException("远端创建任务，互斥代码MutexCode必填，建议结合JoinToken获得唯一码！");
            }

            //查询这个节点下有没有这个任务
            var find = _dbContext.TaskInfo.Where(x => x.IsEnable && x.Groups == input.Groups && x.MutexCode == input.MutexCode).OrderByDescending(x => x.Id).AsNoTracking().FirstOrDefault();
            if (find != null && find != default)
            {
                throw new PasteTimerException($"当前已经有一个MutexCode:{input.MutexCode}这样的远端任务了，请勿重复添加！");
            }

            var node = _dbContext.NodeInfo.Where(x => x.Group == input.Groups).AsNoTracking().FirstOrDefault();
            if (node == null || node == default)
            {
                throw new PasteTimerException($"没有找到节点{input.Groups}的信息，无法创建当前这个任务！");
            }
            if (node.JoinToken != input.JoinToken)
            {
                throw new PasteTimerException($"当前任务指定的JoinToken参数错误，没有和对应的节点{node.Group}的JoinToken相匹配！");
            }
            //var _userid = base.ReadCurrentAdminId();
            var newu = ObjectMapper.Map<TaskInfoAddDto, TaskInfo>(input);
            newu.FileVersion = 1000;
            if (input.TickRegex.Contains("*"))
            {
                newu.TaskType = TaskType.OnTime;
                newu.TickSecond = 0;
            }
            else
            {
                _modelHelper.ReadTickSecond(newu);
            }
            try
            {
                //_dbContext.Database.BeginTransaction();
                newu.UserId = 0;
                _dbContext.Add(newu);
                await _dbContext.SaveChangesAsync();
                //_dbContext.Add(new TaskBindUser
                //{
                //    CreateDate = DateTime.Now,
                //    Create_UserId = _userid,
                //    TaskId = newu.Id,
                //    UserId = _userid
                //});
                //await _dbContext.SaveChangesAsync();
                //await _dbContext.Database.CommitTransactionAsync();
                var backinfo = ObjectMapper.Map<TaskInfo, TaskInfoDto>(newu);
                _channelHelper.WriteStatus(new StatusModel() { Action = 1, ModelType = 1, body = Newtonsoft.Json.JsonConvert.SerializeObject(backinfo) });
                return backinfo;
            }
            catch (Exception exl)
            {
                //_dbContext.Database.RollbackTransaction();
                Logger.LogException(exl);
                throw;
            }
        }

        /// <summary>
        /// 读取是否有这个远端任务
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        [HttpPost]
        public async Task<TaskInfoDto> read(PasteTaskPointModel input)
        {
            if (String.IsNullOrEmpty(input.Groups))
            {
                throw new PasteTimerException("远端创建的任务需要指定节点，表示使用哪个节点执行这个任务！");
            }
            if (input.Groups.Contains(","))
            {
                throw new PasteTimerException("远端任务，不支持多个节点执行，只能指定其中一个节点执行！");
            }

            if (input.Groups == "*" || input.Groups == "slave")
            {
                throw new PasteTimerException("远端创建的任务不允许指定*或者slave节点执行任务！");
            }

            if (String.IsNullOrEmpty(input.MutexCode))
            {
                throw new PasteTimerException("远端创建任务，互斥代码MutexCode必填，建议结合JoinToken获得唯一码！");
            }

            //查询这个节点下有没有这个任务
            var find = await _dbContext.TaskInfo.Where(x => x.IsEnable && x.Groups == input.Groups && x.MutexCode == input.MutexCode).OrderByDescending(x => x.Id).AsNoTracking().FirstOrDefaultAsync();
            if (find != null && find != default)
            {
                var backinfo = ObjectMapper.Map<TaskInfo, TaskInfoDto>(find);
                //_channelHelper.WriteStatus(new StatusModel() { Action = 1, ModelType = 1, body = Newtonsoft.Json.JsonConvert.SerializeObject(backinfo) });
                return backinfo;
                //throw new PasteTimerException($"当前已经有一个MutexCode:{input.MutexCode}这样的远端任务了，请勿重复添加！");
            }
            else
            {
                throw new PasteTimerException("没有找到这样的任务！");
            }
        }

        /// <summary>
        /// 用于远端终止某一个任务
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        [HttpPost]
        public async Task<string> stop(PasteTaskPointModel input)
        {
            if (String.IsNullOrEmpty(input.Groups))
            {
                throw new PasteTimerException("远端创建的任务需要指定节点，表示使用哪个节点执行这个任务！");
            }
            if (input.Groups.Contains(","))
            {
                throw new PasteTimerException("远端任务，不支持多个节点执行，只能指定其中一个节点执行！");
            }

            if (input.Groups == "*" || input.Groups == "slave")
            {
                throw new PasteTimerException("远端创建的任务不允许指定*或者slave节点执行任务！");
            }

            if (String.IsNullOrEmpty(input.MutexCode))
            {
                throw new PasteTimerException("远端创建任务，互斥代码MutexCode必填，建议结合JoinToken获得唯一码！");
            }

            //查询这个节点下有没有这个任务
            var find = _dbContext.TaskInfo.Where(x => x.IsEnable && x.Groups == input.Groups && x.MutexCode == input.MutexCode).OrderByDescending(x => x.Id).AsNoTracking().FirstOrDefault();
            if (find != null && find != default)
            {
                _dbContext.Attach(find);
                find.IsEnable = false;
                await _dbContext.SaveChangesAsync();
                //通知，这个任务被移除了，虽然是软删除
                _channelHelper.WriteStatus(new StatusModel() { Action = 3, ModelType = 1, body = $"{find.Id}" });
            }
            else
            {
                throw new PasteTimerException($"没有基于现有的信息找到这样的任务，你可以执行创建！！！");
            }

            return "停止成功!";
        }

        /// <summary>
        /// 读取本地时间
        /// </summary>
        /// <returns></returns>
        [HttpGet]
        public dynamic date()
        {
            return $"DateTime.Now:{DateTime.Now} DateTime.Now:{DateTime.Now} DateTimeOffset.Now:{DateTimeOffset.Now} DateTime.Now:{DateTime.Now}";
        }

        /// <summary>
        /// 读取本地时间戳
        /// </summary>
        /// <returns></returns>
        [HttpGet]
        public long utctime()
        {
            return DateTimeOffset.Now.ToUnixTimeSeconds();
        }

        /// <summary>
        /// 上传文件
        /// </summary>
        /// <returns></returns>
        [HttpPost]
        [TypeFilter(typeof(RoleAttribute), Arguments = new object[] { "data", "update" })]
        [Route("/api/task/upload")]
        public dynamic upload([FromServices] IOptions<TaskConfig> config)
        {
            var _config = config.Value;
            //版本号咋办?
            var result = false;
            var msg = String.Empty;
            if (base.Request.Form != null)
            {
                if (base.Request.Form.Files != null)
                {
                    if (base.Request.Form.Files.Count > 0)
                    {
                        for (var k = 0; k < base.Request.Form.Files.Count; k++)
                        {
                            var finput = base.Request.Form.Files[k];
                            var filename = $"{Guid.NewGuid().ToString().Replace("-", "")}";
                            var url = $"/api/task/load?file={filename}";
                            var save = $"{_config.UploadDir}/{filename}.zip";
                            if (!System.IO.Directory.Exists(_config.UploadDir)) { System.IO.Directory.CreateDirectory(_config.UploadDir); }
                            using (System.IO.FileStream fs = new System.IO.FileStream(save, System.IO.FileMode.Create))
                            {
                                finput.CopyTo(fs);
                                fs.Flush();
                                fs.Dispose();
                            }
                            result = true;
                            msg = url;
                        }
                    }
                }
            }
            return new
            {
                result = result,
                msg = msg
            };
        }

        /// <summary>
        /// 下载文件
        /// </summary>
        /// <param name="file"></param>
        /// <param name="config"></param>
        /// <returns></returns>
        /// <exception cref="Exception"></exception>
        [HttpGet]
        [Route("/api/task/load")]
        public IActionResult down(string file, [FromServices] IOptions<TaskConfig> config)
        {
            var _config = config.Value;
            checkmodel(_config);
            var path = $"{_config.UploadDir}/{file}.zip";
            if (System.IO.File.Exists(path))
            {
                using var stream = System.IO.File.OpenRead(path);
                var bytes = new byte[stream.Length];
                stream.Read(bytes, 0, bytes.Length);
                //var stream = System.IO.File.OpenRead(path);
                return File(bytes, "application/octet-stream", $"{file}.zip");
            }
            else
            {
                base.Response.StatusCode = 500;
                throw new Exception("下载的资源不存在... .. .");
            }
        }

        /// <summary>
        /// 检查是否是集群模式
        /// </summary>
        /// <exception cref="Exception"></exception>
        private void checkmodel(TaskConfig _config)
        {
            //校验stoken
            if (base.HttpContext.Request.Headers.ContainsKey("stoken"))
            {
                //后续可以改成会过期的方式，进一步提升安全，甚至可以使用证书验证
                if (base.HttpContext.Request.Headers["stoken"].ToString() != _config.PublicToken)
                {
                    Logger.LogError($"请求有误，携带的header.stoken:{base.HttpContext.Request.Headers["stoken"]}和配置:{_config.PublicToken}的不符!");
                    throw new Exception("Header stoken Error!");
                }
            }
            else
            {
                Logger.LogError("请求有误，未携带header.stoken!");
                throw new Exception("Header stoken Not Found, Error!");
            }
        }

    }
}
